どこでも RUN_JavaScriptBT with_HTML&CSS
https://i.gyazo.com/8627e67023abecb5e78d23e4cb54e072.png
メニューボタンに追加した RunButton をクリックすると
Scrapboxの現在表示されているページに書かれた、HTML、CSS を読み込み、
その後、JavaScript を読み込んで実行する という UserScript。
code:RUN.html 無ければ無くてもよい。
code:RUN.css 無ければ無くてもよい。
code:RUN.js 必須。無いとエラーになる。
各 code: の名前は固定。
Scrapbox のページごとに、プログラムを充てることができる。
RUN実行中は、左上のアイコンマークが回転する。
もう一度 RunButton をクリックすると ページがリロードされて、読み込んだ HTML、CSS、JavaScript はリセットされる。
運用はリスクを伴うので要注意。
これで、Scrapbox をプログラムウィジェット群で武装したツールボックス化できるのかも?
個人的には、このニーズへの対応として考えたもの。
制限事項
ブラウザの一つのウインドウ内に一つのRUNのみ限定する。多重起動はできない。
異なるページでの別の HTML、CSS、JavaScript も同時には起動できない。
CSSのスタイルに上書きがある場合など、追加の順番で挙動が変わってしまう。
そのような干渉を管理するのが難しかったので、単発での実行に限定することにした。
RUN.html 内に JavaScript を組込んだものは実行されない。(これはブラウザの仕様)
HTML、CSS、JavaScript は、RUN.html、RUN.css、RUN.js の順番で読み込まれる。
RUN.html に 関数呼び出しの設定(イベントハンドリング処理など)を記述しても機能しない。
RUN.html の読み込み時に RUN.js で記載されている該当関数がまだ存在しないから。
RUN.js の側で、イベントハンドリング関数などを対応させるような書き方が必要!
補足
ページにアクセス権があればプロジェクトメンバー誰でもが実行できることになり、スクリプトの実行権がMyページオーナーに限定されない環境になってしまう。これは、Scrapbox の仕様的にマズイかも知れない。また、Bookmarkletでは多重起動の防止が難かった。
なわけで、 UserScript版に改めた。
以下の script.js を Myページに書く。
code:script.js
// ピっと鳴って ページリロードして 読み込んだスクリプトをリセットする
window.BeepAndReload = function() {
var context = new AudioContext();
var oscillator = context.createOscillator();
oscillator.type = "sine";
oscillator.frequency.value = 880;
oscillator.connect(context.destination);
oscillator.start();
var beepDuration = 50; // Beep音の長さを変数に代入
setTimeout(function () {
oscillator.stop();
// Beep音が鳴り終わった後にリロードを行う
setTimeout(function () {
location.reload(); // ページのリセット
}, beepDuration);
}, beepDuration + 100);
}
// 左上のマークが回転する (RUN.js 実行中を表すため)
window.BMRunningNow = function() {
// CSSの .brand-icon の表示画像を取得
var img = document.querySelector('.brand-icon');
var deg = 0;
setInterval(function() {
img.style.transform = 'rotate(' + deg + 'deg)';
deg = (deg - 6) % 360;
}, 20);
}
// RUN.html RUN.css 読み込み
function loadHTML(htmlurl) {
return new Promise((resolve, reject) => {
fetch(htmlurl)
.then(response => response.text())
.then(data => {
if (!document.documentElement.innerHTML.includes(data)) {
var div = document.createElement('div');
div.innerHTML = data;
document.body.appendChild(div);
}
resolve();
});
});
}
function loadCSS(cssurl) {
return new Promise((resolve, reject) => {
var styleSheets = Array.from(document.styleSheets).map(styleSheet => styleSheet.href);
if (!styleSheets.includes(cssurl)) {
var head = document.getElementsByTagName('HEAD')0; var link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = cssurl;
head.appendChild(link);
resolve();
}
});
}
// Runボタン
var clickRunRunBT = 0;
// UserScript でボタンを追加する
scrapbox.PageMenu.addMenu({
title: 'RunButton',
onClick: () => { // ボタン押されたとき
// RUN がすでに動いていたら
if (clickRunRunBT === 1) {
clickRunRunBT = 0;
BeepAndReload(); // 多重起動禁止! リセットする
} else {
// RUN が動いていない状態で RUN ボタンが押されたら
var RUNcurrentUrl = decodeURIComponent(window.location.href);
// プロジェクト名/ページ名を切り出す
// HTML CSS JavaScript 各 URLにする
// HTML と CSS を読み込む。無くてもエラーにならない。
.then(() => {
// RUN.js を読み取って、スクリプトに追加して間接的に実行する
clickRunRunBT = 1;
BMRunningNow(); // クルクルサイン
// Fetch APIを使用して JavaScriptを取得
fetch(RUNjsurl)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
var existingScript = document.querySelector(script[src="${RUNjsurl}"]);
if (!existingScript) {
var elm = document.createElement('script');
elm.setAttribute('src', RUNjsurl);
elm.onload = function() {
this.remove();
};
document.body.appendChild(elm);
}
})
.catch(error => {
alert(`RUN.js にアクセスできません。\n
おそらくこのページに code:RUN.js が書かれていないものと思われます。`);
// RUN.js は必須
BeepAndReload(); // なくってもリセット
});
});
}
}
})
サンプル
Scrapbox の任意のページに 以下のcode: をコピーして、RUNボタンをクリックすると実行される。
ページの背景色を変更する。
スライダーバーによってページの文字サイズを動的に変更する。
code:RUN.html
<input type="range" id="myRange" min="50" max="150" value="100" step="10" />
<output id="rangeValue"></output>
code:RUN.css
/* Settinsページの style.css に追加されたかのように動く */
/* 実際にページ編集がされるわけではない */
/* 既存の要素がある場合はRUN実行中だけ上書きされる */
:root {
--RUN-My-Font-Size: 100%;
}
.page {
background-color: var(--RUN-My-color) !important;
}
.line {
font-size: var(--RUN-My-Font-Size) !important;
}
code:RUN.css
/* スライダーバーのスタイル設定 */
cursor: 50px;
width: 100%;
border-radius: 5px;
position: absolute;
top: 50px;
left: 90px;
width: 300px;
}
position: absolute;
top: 60px;
left: 410px;
}
code:は複数に分割して書いても、繋げて読まれる。
コメントやボツプログラムを code:の外に書くことができる。
code:RUN.js
// ページの先頭へスクロールを戻す
window.scrollTo(0, 0);
document.getElementById('myRange').addEventListener('input', function() {
rangeop(this.value);
});
let slider = document.getElementById('myRange');
let output = document.getElementById('rangeValue');
// 初期値設定 と その表示
slider.value = 100;
output.textContent = slider.value + "%";
// 出力要素のテキストを更新するイベントハンドラ
slider.oninput = function() {
output.textContent = this.value+ "%";
}
function rangeop(val) { // CSS の変数を書き換えている
var svss = val.toString() + '%';
var fontSize = svss.toString() + '%';
document.documentElement.style.setProperty("--RUN-My-Font-Size", svss);
}